今天嘗試把之前的「Port Scanner 專案」加上 Console Summary(掃描總結報告)。
掃描完所有的 port,不只是一行一行輸出結果,還會在最後整理:
Host (主機名稱)
掃描的 Port 範圍
總共幾個 Port
有幾個開啟 (OPEN)
有幾個關閉 (CLOSED)
花了多少時間 (Elapsed)
額外還做了一個 漂亮的表格輸出(類似 ASCII table)。
package day1.day1;
import java.util.;
import java.util.concurrent.;
import java.net.;
import java.io.;
public class Day27Demo {
static class Result {
final int port;
final String status;
Result(int port, String status) { this.port = port; this.status = status; }
}
private static String checkPort(String host, int port,int timeoutMs) {
try (Socket socket = new Socket()){
socket.connect(new InetSocketAddress(host,port),timeoutMs);
return "OPEN";
}catch (IOException e){
return "CLOSED";
}
}
public static void main(String[] args) throws Exception {
Scanner sc = new Scanner(System.in);
System.out.print("Host(e.g. localhost): ");
String host = sc.nextLine().trim();
System.out.print("START port:");
int start= Integer.parseInt(sc.nextLine().trim());
System.out.print("END port:");
int end= Integer.parseInt(sc.nextLine().trim());
System.out.print("TIMEOUT ms (e.g. 200): ");
int timeoutMs = Integer.parseInt(sc.nextLine().trim());
final int total = Math.max(0, end - start + 1);
if (total == 0) {
System.out.println("No ports to scan."); return; }
ExecutorService executor = Executors.newFixedThreadPool(10);
List<Result> results = Collections.synchronizedList(new ArrayList<>());
long startTime = System.currentTimeMillis();
CountDownLatch latch = new CountDownLatch(total);
for (int p = start; p<=end; p++) {final int port = p;
executor.execute(() -> {
String status = checkPort(host, port, timeoutMs);
results.add(new Result(port, status));
System.out.printf("\rScanned port %d/%d", (total - latch.getCount() + 1), total);
latch.countDown();
});
}
latch.await();
executor.shutdown();
long elapsed = System.currentTimeMillis() - startTime;
long openCount = results.stream().filter(r -> r.status.equals("OPEN")).count();
long closedCount = total-openCount;
System.out.println("\n\n===== Scan Summary =====");
System.out.printf("Host : %s%n", host);
System.out.printf("Ports : %d - %d (total %d)%n", start, end, total);
System.out.printf("OPEN : %d%n", openCount);
System.out.printf("CLOSED : %d%n", closedCount);
System.out.printf("Elapsed : %.2f seconds%n", elapsed / 1000.0);
System.out.println("========================");
// 如果想要漂亮一點的表格:
System.out.println("\nPort Status Table:");
System.out.println("+-------+--------+");
System.out.println("| Port | Status |");
System.out.println("+-------+--------+");
for (Result r : results) {
System.out.printf("| %-5d | %-6s |%n", r.port, r.status);
}
System.out.println("+-------+--------+");
}
}

練習後學到:
CountDownLatch:
它是 Java 並發工具(java.util.concurrent) 的一個類別。
你可以把它想成「一個倒數計時器」或者「一扇門,有很多把鎖」。
它的作用是:等到某些工作都完成之後,再繼續往下跑程式。
new CountDownLatch(total);
這行程式的意思是:
「建立一個倒數計數器,初始值是 total。」
total 通常代表 -> 有多少個任務/工作要做。
每次一個工作完成時,會呼叫 latch.countDown(); → 把計數器減 1。
當計數器變成 0,表示所有工作都完成了,等待的程式就會繼續往下執行。
System.out.printf("\rScanned port %d/%d", (total - latch.getCount() + 1), total);
latch.countDown();
\r 叫做 Carriage Return,意思是「把游標移到這一行的最前面」。
它不會換行,只是回到行首。
這樣可以做到「在同一行更新進度」,不會一直印很多行。
(total - latch.getCount() + 1)
這一段是算「目前做到第幾個」。
latch.getCount() → 目前倒數計時器還剩多少(例如還有 9 個工作沒做)。
total - latch.getCount() → 已經完成了多少個。
+1 → 因為正在處理當前這個任務,所以要加回去。
假設 total = 100,latch.getCount() = 90(還有 90 個沒做)
計算 → 100 - 90 + 1 = 11
表示「現在是第 11 個任務」。
latch.countDown();
這是把倒數計時器 -1,代表「這個任務完成了」。
如果原本是 100 → 變成 99。
當最後減到 0 的時候,所有等待 latch.await() 的程式就會繼續執行。
學到不是只會「功能跑出來就好」,還要會做 總結 / 報告,才能讓使用者更快理解結果。美化 Console 輸出很重要,因為 CLI 程式通常給人第一印象就是「排版好不好讀」。